package chapter08;

/**
 * @Author: insight
 * @Description: 定义简单的泛型类
 * @LastDate: 2020/11/23
 */
public class Test01_02_SimpleExample<T, U> {
    private T first;
    private U second;

    public Test01_02_SimpleExample() { first = null; second = null; }

    public Test01_02_SimpleExample(T first, U second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() { return first; }
    public U getSecond() { return second; }



    public static void main(String[] args) {
        var test = new Test01_02_SimpleExample<Integer, String>(1, "insight");
        System.out.println(test.getFirst().getClass().getName());
        System.out.println(test.getSecond().getClass().getName());

        var middleStr = Alg.getMiddle("1", "2", "3");
        // var middleStr = Alg.<String>getMiddle("1", "2", "3");

        /*
         * 偶尔会出现这种调用泛型方法的错误
         * Incompatible types: Serializable & Comparable <? extends Serializable & Comparable<?>> is not convertible to String
         * 参数类型不同时，编译器会将参数自动装箱，然后寻找他们的超类型
         */
        // String errStr = Alg.getMiddle("1", 2, null);
        // var errStr = Alg.getMiddle("1", 2, null); //no err
    }
}

/**
 * @Auther: insight
 * @Description: 定义简单的泛型方法
 * @LastDate: 2020/11/23
 */
class Alg {
    /**
     * 泛型类的具体使用
     * 定义一个返回类型为泛型类的方法
     * 用于寻找类型为泛型类的数组中的最大值
     */
    public static Test01_02_SimpleExample<Integer, String> findMax(Test01_02_SimpleExample<Integer, String>[] a) {
        if (a.length == 0 || a[0].getSecond().length() == 0) {
            return null;
        }

        Integer maxInt = a[0].getFirst();
        String maxStr = a[0].getSecond();
        for (var e : a) {
            if (maxInt.compareTo(e.getFirst()) < 0) {
                maxInt = e.getFirst();
            }
            if (maxStr.compareTo(e.getSecond()) < 0) {
                maxStr = e.getSecond();
            }
        }
        // 可以不用写明类型 因为方法类型已写清楚了
        return new Test01_02_SimpleExample<>(maxInt, maxStr);
    }

    /**
     * 泛型方法的定义
     * 泛型方法的焦点在于方法的泛型
     * 因此泛型方法不需要在泛型类中定义
     *
     * 类型变量放在返回类型之前
     * 调用时可以写明类型，也可以不写
     */
    public static <T> T getMiddle(T... a) {
        return a[a.length >> 1];
    }

    public static <T extends Comparable> int getMin(T[] a) {
        if (a == null || a.length == 0) {
            return -2;
        }

        return a[0].compareTo(a[1]);
    }
}
